/**
 * {@link WifiDiscovererService} is responsible for scanning the wifi
 * network and gathering measurement so that the server can try to locate the
 * client.
 * 
 * @author SimpliX
 * 
 */
package ch.simplix.android.net.discoverer.wifi;

import java.util.List;
import java.util.concurrent.TimeUnit;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import ch.simplix.android.repetitive.service.implementation.ARepetitiveService;

/**
 * {@link WifiDiscovererService} is responsible for scanning the wirless network and
 * gathering measurement so that the server can try to locate the client.
 * 
 * @author Arno Fiva (fivaa@student.ethz.ch)
 * 
 */
public class WifiDiscovererService extends ARepetitiveService {

	/** Action being broadcasted whenever a new measurement was retrieved */
	public static String WIFI_ACTION = "ch.simplix.android.net.discoverer.wifi.WiFi_DISCOVERER_SERVICE";
	private static final String TAG = WifiDiscovererService.class
	.getSimpleName();
	private boolean wasWIFIAdapterDisabled;

	private WifiManager wifi;
	private WiFiMeasurement latestMeasurement;

	
	/**
	 * {@inheritDoc}
	 */
	@Override
	public void onCreate() {
		super.setUpdateInterval(60, TimeUnit.SECONDS);
	}
		

	@Override
	public void onDestroy() {
		super.onDestroy();
		stop();

	}
	
	/**
	 * Start scanning immediately
	 */
	@Override
	public void onStart(Intent intent) {
		start();		
	}
	
	private void start() {
		if (wifi != null)
			return;
		wifi = (WifiManager) getSystemService(Context.WIFI_SERVICE);
		registerReceiver(stateReceiver, new IntentFilter(
				WifiManager.WIFI_STATE_CHANGED_ACTION));
		registerReceiver(resultReceiver, new IntentFilter(
				WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
	}
	
	public void discover(){
		if(!isRunning()){
			start();
			scan();
		}
	}

	@Override
	public void onStop() {
		stop();
		
	}
	
	private void stop() {
		Log.d(TAG, "stopping");
		if (wifi == null)
			return;
		unregisterReceiver(stateReceiver);
		unregisterReceiver(resultReceiver);
		if (wasWIFIAdapterDisabled) {
			wifi.setWifiEnabled(false);
		}
		wifi = null;
		Log.d(TAG, "stop");

	}

	
	/**
	 * Retrieve measurement from last scan.
	 * 
	 * @return {@link WiFiMeasurement}
	 */
	public WiFiMeasurement getLatestMeasurement() {
		return latestMeasurement;
	}

	
	@Override
	public IBinder onBind(Intent intent) {
		return mBinder;
	}

	/**
	 * @see Binder
	 */
	public class LocalBinder extends Binder {
		public WifiDiscovererService getService() {
			return WifiDiscovererService.this;
		}
	}

	private final LocalBinder mBinder = new LocalBinder();

	/**
	 * Try to enable the wifi adapter if it is not already enabled or not
	 * currently being enabled. Otherwise start the scanning directly.
	 */
	private void scan() {
		if (!wifi.isWifiEnabled()) {
			if (wifi.getWifiState() != WifiManager.WIFI_STATE_ENABLING) {
				wasWIFIAdapterDisabled = true;
				if (!wifi.setWifiEnabled(true))
					return;
			}
		} else {
			wifi.startScan();
		}
	}

	/**
	 * {@link BroadcastReceiver} for reacting on the current wifi state. If the
	 * wifi adapter is enabled, start a scan.
	 */
	private final BroadcastReceiver stateReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			if (wifi!=null && wifi.isWifiEnabled())
				scan();
		}

	};

	/**
	 * {@link BroadcastReceiver} for retrieving scanning results and to creating
	 * a new {@link WiFiMeasurement}. The measurement is then stored in
	 * {@link WiFiMeasurement}.
	 */
	private final BroadcastReceiver resultReceiver = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			
			//Do not listen to broadcast when not initiated scan
			//When no network is available, broadcasts are sent every few seconds
			WifiManager localWiFi=wifi;
			if(localWiFi==null) {				
				return;
			}
			
			List<ScanResult> results = localWiFi.getScanResults();
			WiFiMeasurement measurement = new WiFiMeasurement();

			for (ScanResult result : results) {

				WiFiReading reading = new WiFiReading();
				reading.setBssid(result.BSSID);
				reading.setSsid(result.SSID);
				reading.setRssi(result.level);
				reading.setFrequency(result.frequency);
				reading.setInfrastructure(!result.capabilities.contains("[IBSS]"));
				reading.setEncryptionEnabled(result.capabilities.contains("WEP")
						|| result.capabilities.contains("WPA")
						|| result.capabilities.contains("-EAP-"));				
				measurement.addWiFiReading(reading);
			}

			latestMeasurement = measurement;

			Intent wifiIntent = new Intent(WIFI_ACTION);
			sendBroadcast(wifiIntent);

		}

	};

	@Override
	public void onRepeat() {
		scan();		
	}
}